/*
 * $Id: ReflectionLayout.java 353 2008-04-14 11:48:40Z jasta $
 *
 * Copyright (C) 2008 Josh Guilfoyle <jasta@devtcg.org>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 */

package org.devtcg.five.music.widget;

import java.util.Map;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ComposeShader;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RectShape;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewInflate;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.RelativeLayout;

/**
 * Custom layout to display "full-screen" cover artwork with a
 * reflection effect.  This layout should contain only one view, which
 * will be placed over the bottom refection.
 */
public class ReflectionLayout extends ViewGroup
{
	private static final String TAG = "ReflectionLayout";
	
	private OnClickListener mOnImageTouchListener;
	
	private Animation mFadeIn;
	private Animation mFadeOut;

	private Paint mReflect = new Paint(Paint.FILTER_BITMAP_FLAG);
	private Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);

	private Bitmap mBitmap;
	
	/* Cache which bitmap we made the shader for so that we can detect
	 * when it changes. */
	private boolean mShaderDirty = true;

	private Rect mImageDim = new Rect();

	private Rect mReflectionClip = new Rect();
	private Rect mReflectionDim = new Rect();
//	private float mScaleX;
//	private float mScaleY;
//	private double mPortionY;
	
	private float mScaleFactor;
	private int mScaleBitmapHeight;
	private float mReflectPortion;
	
	public ReflectionLayout(Context context)
	{
		super(context);
		init();
	}

	public ReflectionLayout(Context context, AttributeSet attrs, Map inflateParams)
	{
		super(context, attrs, inflateParams);
		init();
	}

	public ReflectionLayout(Context context, AttributeSet attrs, Map inflateParams, int defStyle)
	{
		super(context, attrs, inflateParams, defStyle);
		init();
	}

	private void init()
	{
		mFadeIn = AnimationUtils.loadAnimation(mContext,
		  android.R.anim.fade_in);
		mFadeOut = AnimationUtils.loadAnimation(mContext,
		  android.R.anim.fade_out);
	}

	public void setOnImageTouchListener(OnClickListener l)
	{
		mOnImageTouchListener = l;
	}

	public boolean onTouchEvent(MotionEvent ev)
	{
		if (mOnImageTouchListener == null)
			return false;

		if (ev.getAction() != MotionEvent.ACTION_DOWN)
			return false;

		/* Don't invoke on touches in the reflection bar. */
		if (ev.getY() >= mScaleBitmapHeight)
			return false;

		mOnImageTouchListener.onClick(this);
		return true;
	}

	public void displayTopChild()
	{
		assert getChildCount() > 0;

		View v = getChildAt(0);

		if (v.getVisibility() == View.VISIBLE)
			return;

		v.startAnimation(mFadeIn);
		v.setVisibility(View.VISIBLE);
	}

	public void hideTopChild()
	{
		assert getChildCount() > 0;

		View v = getChildAt(0);

		if (v.getVisibility() == View.GONE)
			return;

		v.startAnimation(mFadeOut);
		v.setVisibility(View.INVISIBLE);
	}

	public void setImageURI(Uri uri)
	{
		try
		{
			mBitmap = BitmapFactory.decodeStream
			  (mContext.getContentResolver().openInputStream(uri));
		}
		catch (Exception e)
		{
			Log.d(TAG, "Unable to read content: " + uri);
		}

		tryMakeShader();
	}

	public void setImageResource(int id)
	{
		mBitmap = BitmapFactory.decodeResource(getResources(), id);
		tryMakeShader();
	}

	private void tryMakeShader()
	{
//		if (mReflectPortion > 0.0)
//			makeShader();

		mShaderDirty = true;
		requestLayout();
		invalidate();
	}

	private void makeShader()
	{
		if (mShaderDirty == false)
			return;

		if (mBitmap == null)
			return;
		
		Log.d(TAG, "makeShader()");
		
		float y0 = (float)mBitmap.getHeight() * (1.0f - mReflectPortion);
		float y1 = (float)mBitmap.getHeight();

		LinearGradient grad =
		  new LinearGradient(0, y0, 0, y1, 0x66000000, 0x33000000, TileMode.CLAMP);

		BitmapShader bitmap = 
		  new BitmapShader(mBitmap, TileMode.REPEAT, TileMode.REPEAT);

		ComposeShader shader =
		  new ComposeShader(bitmap, grad, new PorterDuff(PorterDuff.Mode.DST_OUT));

		mReflect.setShader(shader);
		mShaderDirty = false;
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b)
	{
		makeShader();
//		configureBounds();

		int selfw = getMeasuredWidth();
		int selfh = getMeasuredHeight();

		View top = getChildAt(0);
		int toph = top.getMeasuredHeight();

		top.layout(0, 0, selfw, toph);

		View bottom = getChildAt(1);
		int bottomh = bottom.getMeasuredHeight();

		bottom.layout(0, selfh - bottomh, selfw, selfh);		
	}

	@Override
	protected void onMeasure(int widthSpec, int heightSpec)
	{
		int w = View.MeasureSpec.getSize(widthSpec);
		int h = View.MeasureSpec.getSize(heightSpec);

		int bw, bh;

		if (mBitmap != null)
		{
			bw = mBitmap.getWidth();
			bh = mBitmap.getHeight();
		}
		else
		{
			bw = w;
			bh = h;
		}

		mScaleFactor = (float)w / (float)bw;
		mScaleBitmapHeight = (int)((float)bh * mScaleFactor);
		mReflectPortion = ((float)h / (float)mScaleBitmapHeight) - 1.0f;
		
		View top = getChildAt(0);
		top.measure
		  (getChildMeasureSpec(widthSpec, 0, top.getLayoutParams().width),
		   getChildMeasureSpec(heightSpec, 0, top.getLayoutParams().height));

		View bottom = getChildAt(1);
		bottom.measure
		 (getChildMeasureSpec(widthSpec, 0, bottom.getLayoutParams().width),
		  MeasureSpec.makeMeasureSpec(h - mScaleBitmapHeight, MeasureSpec.EXACTLY));

		/* XXX: Our layout doesn't play nice with any other usage than
		 * the one we have in playlist.xml :) */
		assert View.MeasureSpec.getMode(widthSpec) == View.MeasureSpec.EXACTLY;
		assert View.MeasureSpec.getMode(heightSpec) == View.MeasureSpec.EXACTLY;

		setMeasuredDimension
		 (View.MeasureSpec.getSize(widthSpec),
		  View.MeasureSpec.getSize(heightSpec));
	}
	
	/* XXX: This is miserably inefficient and should be improved. */
	@Override
	protected void dispatchDraw(Canvas canvas)
	{
		if (mBitmap == null)
			return;

		canvas.drawBitmap(mBitmap, null, new Rect(0, 0, getWidth(), mScaleBitmapHeight), mPaint);

		canvas.save();
		canvas.translate(0, mScaleBitmapHeight * 2);
		canvas.scale(mScaleFactor, -mScaleFactor);
		canvas.clipRect(0, (int)((float)mBitmap.getHeight() * (1.0f - mReflectPortion)),
		  mBitmap.getWidth(), mBitmap.getHeight());
		canvas.drawRect(new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight()),
		  mReflect);
		canvas.restore();

		View top = getChildAt(0);

		if (top.getBackground() == null)
		{
			Log.d(TAG, "top.getHeight()=" + top.getHeight());

//			RectShape shape = new RectShape();
//			ShapeDrawable d = new ShapeDrawable(shape);
//			d.setShader(new LinearGradient(0, 0, 0, (float)top.getHeight() / 2.0f,
//			  0xe0000000, 0xe000000, TileMode.MIRROR));
//			top.setBackground(d);
			top.setBackgroundColor(0xc0000000);
		}

		View bottom = getChildAt(1);

		if (bottom.getBackground() == null)
		{
			RectShape shape = new RectShape();
			ShapeDrawable d = new ShapeDrawable(shape);
//			d.setShader(new LinearGradient(0, 0, 0, v.getHeight(),
//			  new int[] { 0x45cccccc, 0x45aaaaaa, 0xe0333333, 0xe0000000 },
//			  new float[] { 0.0f, 0.5f, 0.5f, 1.0f }, TileMode.CLAMP));
			d.setShader(new LinearGradient(0, 0, 0, bottom.getHeight(),
			  0x66555555, 0xe0000000, TileMode.CLAMP));
			bottom.setBackground(d);
		}
		
		super.dispatchDraw(canvas);		
	}
}
